<script setup lang="ts">
import {
  DataTableBaseColumn,
  DataTableColumns,
  DataTableFilterState,
  FormInst,
  FormRules,
  NButton,
  NButtonGroup,
  NPopconfirm,
  NSpace,
  NTag,
  NTree,
  PaginationInfo,
  PaginationProps,
  TransferRenderSourceList
} from "naive-ui";
import {SysRole} from "@/api/role/types.ts";
import {roleApi} from "@/api/role";
import {useDictStore} from "@/stores";
import {flattenTree, renderPermission, resetToNull} from "@/utils";
import {SysMenu} from "@/api/menu/types.ts";
import {menuApi} from "@/api";
import {RenderListProps} from "naive-ui/es/transfer/src/interface";

enum ModeTypeEnum {
  ADD = '新增',
  MODIFY = '修改',
  DETAIL = '详情'
}

interface Option {
  type: number
  label: string
  permissionCode: string
  value: string
  children?: Option[]
}

interface PageQuery {
  keyword?: string,
  roleStatus?: number
}

const dictStore = useDictStore();
const pageQuery = reactive<PageQuery>(<PageQuery>{})
const tableLoading = ref(false);
const tableColumns = ref<DataTableColumns<SysRole>>([
  {title: '角色名称', key: 'roleName', resizable: true},
  {title: '角色编码', key: 'roleCode', resizable: true},
  {
    title: '角色状态',
    key: 'roleStatus',
    resizable: true,
    render: row => dictStore.renderTag('common-status', row.roleStatus),
    filter: true,
    filterMultiple: false,
    filterOptions: dictStore.getDictTextOptions('common-status')
  },
  {title: '角色权重', key: 'roleWeight', resizable: true},
  {title: '创建时间', key: 'createTime', resizable: true},
  {title: '操作', key: 'action', align: 'center', render: row => tableActionRender(row)}
])
const tableData = ref<SysRole[]>([])
const tablePagination = reactive<PaginationProps>({
  prefix: (info: PaginationInfo) => {
    return `共 ${info.itemCount} 条记录`
  },
  showSizePicker: true,
  pageSizes: [10, 20, 50, 100],
  itemCount: 0,
  pageCount: 0,
  onChange: (page: number) => {
    tablePagination.page = page
    handlePageQuery()
  },
  onUpdatePageSize: (pageSize: number) => {
    tablePagination.pageSize = pageSize
    tablePagination.page = 1
    handlePageQuery()
  }
})
const drawerDetailShow = ref(false)
const drawerDetailTitle = ref<ModeTypeEnum | null>(null)
const drawerDetailFormRef = ref<FormInst | null>(null)
const drawerDetailFormModel = reactive<SysRole>({})
const drawerDetailFormRules = reactive<FormRules>({
  roleName: {required: true, message: '请输入角色名称', trigger: 'blur'},
  roleCode: {required: true, message: '请输入角色编码', trigger: 'blur'},
})
const drawerDetailSubmitLoading = ref(false)
const modalGrantShow = ref(false)
const modalGrantEntity = reactive<SysRole>({})
const modalGrantOptions = ref<Option[]>([])
const modalGrantChecked = ref<string[]>([])
const modalGrantLoading = ref(false)

onMounted(() => {
  dictStore.loadDict(['common-status', 'menu-type'])
  handlePageQuery()
})

const tableActionRender = (row: SysRole) => {
  return h(NButtonGroup, {size: 'small'}, {
    default: () => {
      return [
        renderPermission(h(NButton, {
          tertiary: true,
          onClick: () => handleClickDetail(row)
        }, {default: () => '详情'}), 'role:detail'),
        renderPermission(h(NButton, {
          tertiary: true,
          type: 'primary',
          onClick: () => handleClickModify(row)
        }, {default: () => '修改'}), 'role:modify'),
        renderPermission(h(NButton, {
          tertiary: true,
          type: 'info',
          onClick: () => handleClickGrant(row)
        }, {default: () => '授权'}), 'role:grant'),
        h(NPopconfirm, {onPositiveClick: () => handleClickDelete(row)}, {
          trigger: () => renderPermission(h(NButton, {
            tertiary: true,
            type: 'error',
          }, {default: () => '删除'}), 'role:delete'),
          default: () => `确定删除该(${row.roleName}/${row.roleCode})角色吗？`
        })
      ]
    }
  })
}
const handlePageQuery = () => {
  tableLoading.value = true
  roleApi.page({
    ...pageQuery,
    current: tablePagination.page ?? 1,
    size: tablePagination.pageSize ?? 10
  }).then(res => {
    tableData.value = res.data.records
    tablePagination.itemCount = res.data.total
    tablePagination.pageCount = res.data.pages
  }).finally(() => tableLoading.value = false)
}
const handleClickDelete = (row: SysRole) => {
  if (row.id != null) {
    roleApi.delete(row.id).then(res => {
      window.$message?.success(res.message)
      handlePageQuery()
    })
  }
}
const initDrawerDetail = (type: ModeTypeEnum) => {
  drawerDetailTitle.value = type
  drawerDetailShow.value = true
}
const handleClickAdd = () => {
  initDrawerDetail(ModeTypeEnum.ADD)
  drawerDetailFormModel.roleStatus = dictStore.getDictDefault('common-status').dictValue as number
}
const handleClickDetail = (row: SysRole) => {
  if (row.id != null) {
    roleApi.detail(row.id).then(res => {
      Object.assign(drawerDetailFormModel, res.data)
      initDrawerDetail(ModeTypeEnum.DETAIL)
    })
  }
}
const handleClickModify = (row: SysRole) => {
  if (row.id != null) {
    roleApi.detail(row.id).then(res => {
      Object.assign(drawerDetailFormModel, res.data)
      initDrawerDetail(ModeTypeEnum.MODIFY)
    })
  }
}

const transferSourceListRender: TransferRenderSourceList = ({onCheck, pattern}: RenderListProps) => {
  return h(NTree, {
    showLine: true,
    cascade: false,
    style: 'margin: 0 4px;',
    checkable: true,
    selectable: false,
    blockLine: true,
    checkOnClick: true,
    keyField: 'value',
    renderLabel: transferLabelRender,
    data: modalGrantOptions.value,
    pattern,
    checkedKeys: modalGrantChecked.value,
    onUpdateCheckedKeys: (checkedKeys: string[], _, meta: { node: Option, action: 'check' | 'uncheck' }) => {
      let nodes = flattenTree(meta.node.children);
      if (nodes && nodes.length > 0) {
        let keys = nodes.map(node => node.value);
        if (meta.action === 'check') {
          checkedKeys = Array.from(new Set([...checkedKeys, ...keys]));
        } else if (meta.action === 'uncheck') {
          checkedKeys = checkedKeys.filter(key => !keys.includes(key));
        }
      }
      onCheck(checkedKeys)
    }
  })
}
const transferLabelRender = ({option}) => {
  return h(NSpace, {}, {
    default: () => {
      return [
        dictStore.renderTag('menu-type', option.type),
        h('span', {}, {default: () => `${option.label} | ${option.permissionCode}`})
      ]
    }
  })
}
const handleClickGrant = (row: SysRole) => {
  modalGrantShow.value = true
  Object.assign(modalGrantEntity, row)
  menuApi.tree({menuStatus: 1}).then(res => {
    modalGrantOptions.value = convertOption(res.data)
  })
  if (row.id) {
    roleApi.grantedList(row.id).then(res => {
      modalGrantChecked.value = res.data.menuIds
    })
  }
}
const convertOption = (list: SysMenu[]): Option[] => {
  return list.map(item => {
    return {
      value: item.id,
      label: item.menuName,
      type: item.menuType,
      permissionCode: item.permissionCode,
      children: item.children ? convertOption(item.children) : undefined
    } as Option
  })
}
const handleClickSubmit = () => {
  drawerDetailSubmitLoading.value = true
  drawerDetailFormRef.value?.validate((errors) => {
    if (!errors) {
      switch (drawerDetailTitle.value) {
        case ModeTypeEnum.ADD:
          roleApi.add(drawerDetailFormModel).then(res => {
            window.$message?.success(res.message)
            drawerDetailShow.value = false
          })
          break
        case ModeTypeEnum.MODIFY:
          roleApi.modify(drawerDetailFormModel).then(res => {
            window.$message?.success(res.message)
            drawerDetailShow.value = false
          })
      }
    }
  }).finally(() => drawerDetailSubmitLoading.value = false)
}
const handleDrawerClose = () => {
  drawerDetailFormRef.value?.restoreValidation()
  resetToNull(drawerDetailFormModel)
  handlePageQuery()
}
const handleClickGrantSubmit = () => {
  modalGrantLoading.value = true
  roleApi.grant({id: modalGrantEntity.id, menuIds: modalGrantChecked.value}).then(res => {
    window.$message?.success(res.message)
    modalGrantShow.value = false
  }).finally(() => modalGrantLoading.value = false)
}
const handleModalGrantClose = () => {
  modalGrantLoading.value = false
  modalGrantShow.value = false
  modalGrantOptions.value = []
  modalGrantChecked.value = []
  resetToNull(modalGrantEntity)
}
</script>

<template>
  <n-card class="role-manage">
    <template #header>
      <n-space>
        <n-input v-model:value="pageQuery.keyword" placeholder="角色名称/角色编码" clearable/>
        <n-button
            secondary
            @click="handlePageQuery"
            :loading="tableLoading"
            v-p="'role:page'"
        >查询
        </n-button>
        <n-button
            secondary
            type="primary"
            @click="handleClickAdd"
            v-p="'role:add'"
        >新增
        </n-button>
      </n-space>
    </template>
    <n-data-table
        remote
        :loading="tableLoading"
        :columns="tableColumns"
        :data="tableData"
        :pagination="tablePagination"
        @update:filters="(filters: DataTableFilterState, {key}: DataTableBaseColumn) => {
          pageQuery[key] = filters[key]
          handlePageQuery()
        }"
    />
  </n-card>
  <n-drawer
      v-model:show="drawerDetailShow"
      :width="502"
      placement="right"
      :on-after-leave="handleDrawerClose"
      :auto-focus="false"
      :mask-closable="false"
      close-on-esc
  >
    <n-drawer-content :title="drawerDetailTitle" closable>
      <n-form
          ref="drawerDetailFormRef"
          :model="drawerDetailFormModel"
          :rules="drawerDetailFormRules"
          :disabled="drawerDetailTitle === ModeTypeEnum.DETAIL"
      >
        <n-grid :cols="24" :x-gap="12">
          <n-form-item-gi v-if="drawerDetailFormModel.id" :span="24" label="主键">
            <n-tag type="primary">{{ drawerDetailFormModel.id }}</n-tag>
          </n-form-item-gi>
          <n-form-item-gi :span="24" label="角色名称" path="roleName">
            <n-input v-model:value="drawerDetailFormModel.roleName" placeholder="请输入角色名称"/>
          </n-form-item-gi>
          <n-form-item-gi :span="24" label="角色编码" path="roleCode">
            <n-input v-model:value="drawerDetailFormModel.roleCode" placeholder="请输入角色编码"/>
          </n-form-item-gi>
          <n-form-item-gi :span="12" label="角色状态">
            <n-switch v-model:value="drawerDetailFormModel.roleStatus" :checked-value="1" :unchecked-value="0"/>
          </n-form-item-gi>
          <n-form-item-gi :span="12" label="角色权重">
            <n-input-number v-model:value="drawerDetailFormModel.roleWeight" placeholder="请输入角色权重"/>
          </n-form-item-gi>
          <n-form-item-gi :span="24" label="角色描述">
            <n-input type="textarea" v-model:value="drawerDetailFormModel.roleDesc" placeholder="请输入角色描述"/>
          </n-form-item-gi>
        </n-grid>
      </n-form>
      <template v-if="drawerDetailTitle !== ModeTypeEnum.DETAIL" #footer>
        <n-button type="primary" @click="handleClickSubmit" :loading="drawerDetailSubmitLoading">提交</n-button>
      </template>
    </n-drawer-content>
  </n-drawer>
  <n-modal
      :on-after-leave="handleModalGrantClose"
      v-model:show="modalGrantShow"
      title="角色授权"
      w-1000px
      h-600px
      close-on-esc
      closable
      preset="card"
      :auto-focus="false"
      :mask-closable="false"
      footer-class="text-align-right"
  >
    <n-space vertical>
      <n-descriptions label-placement="left">
        <n-descriptions-item label="角色主键">
          <n-tag type="primary" size="small">{{ modalGrantEntity.id }}</n-tag>
        </n-descriptions-item>
        <n-descriptions-item label="角色名称">
          <n-tag type="primary" size="small">{{ modalGrantEntity.roleName }}</n-tag>
        </n-descriptions-item>
        <n-descriptions-item label="角色编码">
          <n-tag type="primary" size="small">{{ modalGrantEntity.roleCode }}</n-tag>
        </n-descriptions-item>
      </n-descriptions>
      <n-transfer
          h400px
          v-model:value="modalGrantChecked"
          :options="flattenTree(modalGrantOptions)"
          :render-source-list="transferSourceListRender"
          :render-target-label="transferLabelRender"
          source-filterable
      />
    </n-space>
    <template #footer>
      <n-button type="primary" @click="handleClickGrantSubmit" :loading="modalGrantLoading">提交</n-button>
    </template>
  </n-modal>
</template>

<style scoped>

</style>
